using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;

namespace Cruel_WoW_Launcher.Core
{
    class Extensions
    {
        public static int GetRemoteGameVersion() => XMLTools.GetNodeAttributeInteger("Launcher", "client_version");

        public static int GetClientGameVersion() => Properties.Settings.Default.ClientVersion;

        public static bool IsNewRemoteClientVersion()
        {
            try
            {
                if (GetRemoteGameVersion() != GetClientGameVersion())
                {
                    LogHandler.WriteToLog($"Доступна новая версия удаленного клиента {GetRemoteGameVersion()}");
                    return true;
                }
            }
            catch (Exception ex)
            {
                LogHandler.WriteToLog($"Ошибка версии удаленного обновления:\r\n{ex.Message}");
            }
            return false;
        }

        public static void UpdateClientVersion()
        {
            Properties.Settings.Default.ClientVersion = GetRemoteGameVersion();
            Properties.Settings.Default.Save();
        }

        public static void ResetClientVersion()
        {
            Properties.Settings.Default.ClientVersion = 0;
            Properties.Settings.Default.Save();
        }

        public static void RemoveHDGraphics()
        {
            foreach (KeyValuePair<string, string> item in Downloader.Data.HDFilesList)
            {
                var filePath = item.Key;
                if (File.Exists(filePath))
                    File.Delete(filePath);
            }
        }

        public static string HexDecodeString(string hex)
        {
            try
            {
                int NumberChars = hex.Length;

                byte[] bytes = new byte[NumberChars / 2];

                for (int i = 0; i < NumberChars; i += 2)
                    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);

                return Encoding.UTF8.GetString(bytes);
            }
            catch { }

            return string.Empty;
        }

        public static string ByteArrayToHex(byte[] bytes, bool upperCase)
        {
            StringBuilder result = new StringBuilder(bytes.Length * 2);

            for (int i = 0; i < bytes.Length; i++)
                result.Append(bytes[i].ToString(upperCase ? "X2" : "x2"));

            return result.ToString();
        }

        public static async Task<string> GetOutputFromURL(string url)
        {
            string result = string.Empty;

            await Task.Run(() =>
            {
                using (WebClient client = new WebClient())
                {
                    result = client.DownloadString(url);
                }
            });
            return result;
        }

        public static bool IsValidURLData(string str)
        {
            if (string.IsNullOrEmpty(str))
                return false;

            if (string.IsNullOrWhiteSpace(str))
                return false;

            if (!str.StartsWith("data="))
                return false;

            return true;
        }

        public static bool IsHDGraphicsEnable() => Properties.Settings.Default.HDGraphics;

        public static string GetLauncherRemotePathURL()
        {
            string result = string.Empty;
            try
            {
                result = XMLTools.GetNodeAttributeString("Launcher", "remote_path");
            }
            catch (Exception ex)
            {
                LogHandler.WriteToLog($"Ошибка URL-адреса удаленной папки Launcher:\r\n{ex.Message}");
            }
            return result;
        }

        public static bool IsStringMD5(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                LogHandler.WriteToLog($"Ввод {input} НЕ является действительным хешем MD5.");
                return false;
            }

            LogHandler.WriteToLog($"Ввод {input} является действительным хешем MD5.");
            return Regex.IsMatch(input, "^[0-9a-fA-F]{32}$", RegexOptions.Compiled);
        }

        public static async Task<bool> IsMD5Different(string fileURL, string localPath)
        {
            bool different = false;

            try
            {
                await Task.Run(() =>
                {
                    if (File.Exists(localPath))
                    {
                        string remoteMD5;
                        string localMD5;

                        // get remote md5
                        LogHandler.WriteToLog("---------------------------------------------");
                        LogHandler.WriteToLog("Проверка MD5-хеша:");
                        LogHandler.WriteToLog(fileURL);
                        LogHandler.WriteToLog(localPath);

                        using (WebClient client = new WebClient())
                        {
                            // correct the root path
                            fileURL = $"{GetLauncherRemotePathURL()}?md5={fileURL.Replace($"{GetLauncherRemotePathURL()}/", string.Empty)}";

                            remoteMD5 = client.DownloadString(fileURL);
                            if (!IsStringMD5(remoteMD5))
                            {
                                LogHandler.WriteToLog($"Результат не является допустимой хэш-строкой md5.: {remoteMD5}");
                                different = true;
                            }

                            client.Dispose();
                        }

                        // get local md5
                        LogHandler.WriteToLog($"Проверка хеша MD5 для локального файла: {localPath}");
                        using (var md5 = MD5.Create())
                        {
                            using (var stream = File.OpenRead(localPath))
                            {
                                localMD5 = ByteArrayToHex(md5.ComputeHash(stream), false);
                                LogHandler.WriteToLog($"Хэш локального файла MD5: {localMD5}");
                            }
                        }

                        // check if hashes match
                        if (remoteMD5 != localMD5)
                        {
                            LogHandler.WriteToLog($"URL-адрес файла {fileURL} отличается от хеша md5 локального файла {localPath}");
                            different = true;
                        }
                        else
                            LogHandler.WriteToLog($"URL-адрес файла {fileURL} НЕ отличается хешем md5 от локального файла {localPath}");

                        LogHandler.WriteToLog("---------------------------------------------");
                    }
                    else
                    {
                        LogHandler.WriteToLog($"Путь к локальному файлу не существует (файл помечен как другой): {localPath}");
                        different = true;
                    }
                });
            }
            catch (Exception ex)
            {
                LogHandler.WriteToLog($"Обнаружена ошибка исключения при проверке хэшей md5.:\r\n{ex.Message}");
            }

            return different;
        }

        public static async Task<long> GetRemoteFileSizeAsync(string url)
        {
            long result = 0;

            await Task.Run(() =>
            {
                try
                {
                    System.Net.WebRequest req = WebRequest.Create(url);
                    req.Method = "HEAD";
                    using (WebResponse resp = req.GetResponse())
                    {
                        if (long.TryParse(resp.Headers.Get("Content-Length"), out long ContentLength))
                            result = ContentLength;
                    }
                }
                catch (Exception ex)
                {
                    LogHandler.WriteToLog($"Ошибка при получении размера удаленного файла:\r\n{ex.Message}");
                }
            });
            return result;
        }
    }
}
